/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file
 * \brief Service implementation of Hub Name Service.
 * \author Antti Maula <antti.maula@tkk.fi>
 */

#define _GNU_SOURCE
#include <string.h>

#include "apservicebase.h"
#include "protocolDispatcher.h"
#include "protocol/fastProtocolHdr.h"
#include "protocol/hubctrlHdr.h"
#include "hubctrl_common.h"
#include "apnameserviceframe.h"
#include "nameservicecore.h"
#include "apsecurity.h"
#include "owndebug.h"
#include "ownutils.h"
#include "msgreplybuffer.h"
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <assert.h>


//*****************************************************************************
//*****************************************************************************
#define GIMNETAP_SERVICE_VERSIONSTRING "NameService-0.3"
//*****************************************************************************
//*****************************************************************************
long unsigned int succesfull_query_count = 0;
long unsigned int failed_query_count = 0;
static int verbose = ODINFO;
static char targetAddress[255];
static int targetTcpPort = 0; // Port to use
static TAPSecurityAuthentication credentials = { "clientnone", 
                                                 10, 
                                                 "apsnone", 
                                                 7, 
                                                 "apnone", 
                                                 6 };
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
static void sanityCheck()
{
  assert(sizeof(TAPNameServiceFrame) == SIZEOF_TAPNAMESERVICEFRAME);
}
//*****************************************************************************

static void processFastProtocolFrame(const TFastProtocolHeader *framehdr,
                                     const unsigned char *payload,
                                     const unsigned int payloadSize,
                                     void *userPtr)
{  
  /*dPrint(ODTEST,"FastProtocolFrame: %p, %p, %d",
    framehdr, payload, payloadSize);*/
  TProtocolDisp *link = (TProtocolDisp *)userPtr;
  unsigned char replybuffer[sizeof(TFastProtocolHeader)+sizeof(TAPNameServiceFrame)];
  TFastProtocolHeader *replyfpframe = (TFastProtocolHeader *)replybuffer;
  TAPNameServiceFrame *replynsframe = (TAPNameServiceFrame *)(replyfpframe+1);

  // Apply and switch fp
  *replyfpframe = *framehdr;
  replyfpframe->source_id = framehdr->target_id;
  replyfpframe->target_id = framehdr->source_id;
  replyfpframe->ttl = FASTPROTOCOL_DEFAULT_TTL;

  // Check that payload size is atleast size of the main frame
  if (payloadSize >= sizeof(TAPNameServiceFrame)) {
    const TAPNameServiceFrame *nsframe = (const TAPNameServiceFrame *)payload;
    
    // Check that service_id matches expected.
    if (nsframe->service_id == GIMNETAP_SERVICE_NAMESERVICE_ID) {
      int result = -1;
      //            dPrint(ODTEST,"Qualified message received from node 0x%08x",
      //           framehdr->source_id);

      // Switch according to request.
      switch(nsframe->request) {
      case KAPNameServiceRequestIdByName:
        //        dPrint(ODTEST,"Message was IdByName");
        
        result = nameservicecoreRequestIdByName(replynsframe, nsframe, framehdr);
        break;

      case KAPNameServiceRequestNameById:
        //        dPrint(ODTEST,"Message was NameById");
        result = nameservicecoreRequestNameById(replynsframe, nsframe, framehdr);
        break;

      case KAPNameServiceRequestRegister:
        //        dPrint(ODTEST,"Message was Register");
        result = nameservicecoreRequestRegister(replynsframe, nsframe, framehdr);
        break;

      case KAPNameServiceRequestUnregister:
        //dPrint(ODTEST,"Message was Unregister");
        result = nameservicecoreRequestUnregister(replynsframe, nsframe, framehdr);
        break;

      default:
        result = nameservicecoreRequestUnknown(replynsframe, nsframe, framehdr);
        dPrint(ODWARN,"Unsupported NameService RequestType %u",
               nsframe->request);
        
        break;
      }

      // Statistics ;)
      if (result) ++failed_query_count;
      else ++succesfull_query_count;
      
      // Reply is always sent.
      /*dPrint(ODTEST,"Reply sent! ReplyBuffer: TargetID:0x%08x, size: %u bytes",
             replyfpframe->target_id,
             replyfpframe->protocol_id.total_size);*/
      protocolDispSendPacket(link,(void*)replybuffer,sizeof(replybuffer));


    } else {
      dPrint(ODWARN,"Mismatching service_id. (0x%04x != 0x%04x) Skip!", 
             nsframe->service_id, GIMNETAP_SERVICE_NAMESERVICE_ID);
    }
  } else {
    dPrint(ODWARN,"Received payload smaller than TAPNameServiceFrame! Skip.");
    
  }
}
//*****************************************************************************

static int InitializeService(TProtocolDisp *link)
{
  // Initialize nameservice Core, giving a rough guess of maximum of
  // 10000 names, minimum validity 60 sec, maximum validity 1 hour
  nameservicecoreInit(10000, 60, 1*60*60);

  return 0;
}
//*****************************************************************************

static void DeinitializeService(TProtocolDisp *link)
{
  nameservicecoreDeinit();
}
//*****************************************************************************

static void GetQueryStats(long unsigned int *s_count,
                   long unsigned int *f_count)
{
  *s_count = succesfull_query_count;
  *f_count = failed_query_count;
}
//*****************************************************************************

void showUsage(const char const *app)
{
}
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************

int main(int argc, char *argv[]) 
{
  int paramParseLoop = 1;
  int r = 0;
  
  // SANITY
  sanityCheck();

  // Set up utils (debugs mostly)
  debugInit();
  debugSetGlobalDebugLvl(verbose);
  debugRemoveMode( DEBUG_MODE_TIMESTAMP_HH_MM_SS | DEBUG_MODE_TIMESTAMP_HH_MM_SS_MS );
  debugAddMode( DEBUG_MODE_TIMESTAMP_DELTA_SS_US );

  // Now; Set to BLOCK ALL SIGNALS
  apservicebaseInitSignalHandler();
  
  // Set info
  apservicebaseSetServiceInfo(GIMNETAP_SERVICE_VERSIONSTRING,
                              GIMNETAP_SERVICE_NAMESERVICE_ID & 0xFF,
                              GIMNETAP_SERVICE_NAMESERVICE_ID);
  apservicebaseSetHubCtrlKey((uint8_t*)"apsnone", 7); // Default; command line overrides.
  apservicebaseSetDefaultOperationTimeout(2000);


  // Call registers
  apservicebaseRegisterFastProtocolHandler(processFastProtocolFrame);
  apservicebaseRegisterServiceInitHandler(InitializeService);
  apservicebaseRegisterServiceDeinitHandler(DeinitializeService);
  apservicebaseRegisterGetQueryStats(GetQueryStats);


  // Print version
  printf("\nGIMnet NameService %s\n\n", GIMNETAP_SERVICE_VERSIONSTRING);
  
  
  // Decode command line parameters.
  while(paramParseLoop) {
    int c;
    c = getopt(argc, argv, "A:u:p:qvh");
    switch(c) {
      
    case 'p':
      // Port to use
      targetTcpPort = atoi(optarg);
      break;
      
    case 'u':
      // Host to
      strncpy(targetAddress, optarg, sizeof(targetAddress)-1);
      targetAddress[sizeof(targetAddress)-1] = '\0';
      break;
      

    
    case 'A': {
      printf("Using AuthenticationFile: '%s'\n", optarg);
      if (apsecurityReadAuthenticationFile(&credentials,
                                           optarg)) {
        printf("Failed to read given Authentication file '%s'! Fatal error, abort!\n", 
               optarg);
        exit(1);
      
      } else {
        apservicebaseSetHubCtrlKey(credentials.secretAPService,
                                   credentials.secretAPServiceSize);
        
      }
      break;
    }
      //////////////////////////////////
      //// GENERIC:                 ////
      //////////////////////////////////

    case 'v':
      // Increase only if not previously set to 0 (by 'quiet')
      if (verbose > 0) verbose++;
      debugSetGlobalDebugLvl(verbose);
      break;
      
    case 'q':
      verbose = 0;
      debugSetGlobalDebugLvl(verbose);
      debugSetLogFilename(NULL);
      break;
      
    case 'h':
    case '?':
      showUsage(argv[0]);
    exit(1);
    break;
    
    case -1:
      paramParseLoop = 0;
      break;
    }
  }

  // Now; got all parameters -> start processing
  // 1. Initialize TCP link
  TProtocolDisp link;
  int v = 0;
  v = protocolDispInit(&link);
  dPrint(ODINFO,"Connecting to '%s' at port %u", targetAddress, targetTcpPort);
  v &= protocolDispConnect(&link, targetAddress, targetTcpPort);
  if (v == PROTOCOLDISPATCHER_OK) {
    // OK!
    r = apservicebaseExecuteService(&link);

  } else {
    // FAILED!
    dPrint(ODSTATUS,"Failed to connect to GIMnetAP, fatal error");

  } 

  dPrint(ODSTATUS,"NameService terminated.\n\n");

  return r;
}
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
